#ifndef GST_PLAY_ENGINE_H__
#define GST_PLAY_ENGINE_H__

#include <QWidget>
#include <string>
#include <QString>
#include <QQueue>
#include <QDateTime>
#include <QUrl>
#include <QMimeDatabase>
#include <QUuid>
#include <QTimer>
#include <gst/gst.h>
#include "log/logger.h"

//媒体资源信息
struct MediaInfo
{
    MediaInfo(){init();}
    void init();

    //媒体播表信息
    QString id;                //媒体源id
    QString url;               //媒体源地址
    QString programType;       //节目类型：plan(播表)、spot（插播）、shim（垫片）、custom（定制）
    QString programName;       //节目单名称（上传播放日志用）
    QString programId;         //节目单ID（上传播放日志用）
    QString mediaType;         //媒体类型（live/video/audio/live/image/web）
    qint64 startPos;           //指定媒体正放/倒放时的起始播放位置(ms)，反向播放时此值必须大于0。如果startPos=0则从0秒开始正向播放
    qint64 endPos;             //指定媒体停止播放位置(ms)，如果endPos>duration或endPos<=0则正常播放到结束，否则正常播放到结尾
    QTime startTime;
    QTime endTime;
    QString sexTag;
    QString ageTag;

    //媒体元数据
    bool isQueryed;             //媒体是否已经查询过元信息
    bool isValid;               //媒体地址是否有效
    qint64 duration;            //媒体总时长(ms)，对于图片等媒体，代表其需要播放的时长
    QString videoFormat;        //视频流格式
    QString audioFormat;        //音频流格式
    QString containerFormat;    //封装格式

    //媒体状态数据
    int errorCount;     //媒体连续播放失败次数
    bool isSeeked;      //媒体是否已经定位过
};

/*
    视频播放器
*/
class VideoPlayer : public QObject
{
    Q_OBJECT
public:
    static QString createMediaPreview(const QString& mediaFilePath,const QSize& size=QSize());
    static bool queryMediaInfo(const QString& url,MediaInfo& mediaInfo,QString& errMsg);
    static QString dumpMediaInfo(const MediaInfo& media);

public:
    explicit VideoPlayer(const QString& logName, QObject* parent=nullptr);
    ~VideoPlayer();

    //播放控制
    void play(MediaInfo media);
    void stop();
    void pause();
    void resume();
    bool step(GstFormat format=GST_FORMAT_TIME,qint64 num=1000);
    bool seek(qint64 pos);

    //播放设置
    WId wId();
    void setWId(WId id);
    bool fill();
    void setFill(bool fill);
    double rate();
    void setRate(double rate);
    bool mute();
    void setMute(bool mute);
    int volume();
    void setVolume(int volume);
    bool enableHWA();
    void setEnableHWA(bool enable);
    bool showFirstFrame();
    void setShowFirstFrame(bool show);
    int posNotifyInterval();
    void setPosNotifyInterval(int intervalMs, bool notifyWithSec=true);
    QSize renderSize();
    void setRenderSize(QSize size);
    QString vsinkName();
    void setVsinkName(const QString& vsink);
    int timeout();
    void setTimeout(int ms);

    qint64 position();
    GstState state();
    MediaInfo* media();
    Logger *logger();

signals:
    void playerPlaying();                   //播放开始
    void playerPaused();                    //播放暂停
    void playerStopped();                   //播放停止
    void playerError(const QString& errMsg);//播放出现错误
    void playerEnded();                     //播放到达结尾
    void requestRetryPlay();                //指示需要重试播放操作
    void positionChanged(qint64 curPoss);   //播放进度改变

private:
    //管线回调
    static gboolean gstBusMessageCallback(GstBus *bus, GstMessage *msg, gpointer data);
    static void gstElementSetupCallback(GstElement *playbin, GstElement *new_ele, gpointer data);
    static void gstSourceElementSetupCallback(GstElement *playbin, GstElement *new_ele, gpointer data);
    static void gstPadAddedCallback(GstElement *src, GstPad *new_pad, gpointer data);
    static int gstDecoderSelectCallback(GstElement *bin, GstPad *pad, GstCaps* caps, GstElementFactory* factory, gpointer data);
    static GstPadProbeReturn gstVideoSinkProbe(GstPad *pad, GstPadProbeInfo *info, gpointer data);

    //管线管理
    void createPipeline();
    void destoryPipeline();

    //状态转换
    void error(const QString& errMsg);
    void changeState(GstState targetState,const QString &reason,bool check);
    void processStateChangeResult(GstState curState);

    //事件处理
    void onStateChangedMessage(GstMessage *msg);
    void onEosMessage(GstMessage *msg);
    void onErrorMessage(GstMessage *msg);
    void onElementMessage(GstMessage* msg);
    void onBufferingMessage(GstMessage *msg);
    void onDurationChangedMessage(GstMessage *msg);
    void onAsyncDoneMessage(GstMessage *msg);
    void onClockLostMessage(GstMessage *msg);
    void onLatencyMessage(GstMessage *msg);

    //播放状态检测
    void startProbeCheck();
    void stopProbeCheck();
    void onProbeCheckTimerTimeout();

    //播放时间控制
    void startEndTimer();
    void stopEndTimer();
    void onEndTimerTimeout();

    //进度通知
    void startPosNotifyTimer();
    void stopPosNotifyTimer();
    void onPosNotifyTimerTimeout();

    //工具函数
    QString dumpPipelineState();

private:
    Logger* m_logger;

    QTimer m_endTimer;
    QTimer m_probeCheckTimer;
    QTimer m_posNotifyTimer;

    GstElement* m_filesrc;
    GstElement* m_pipeline;
    GstElement *m_playsink;
    GstElement* m_videosink;
    QVector<GstPad*> m_playsinkPads;
    guint m_busWatchId;
    guint m_probeId;

    WId m_wid;
    MediaInfo m_mediaInfo;
    QMimeDatabase m_mimeDb;
    int m_volume;
    bool m_mute;
    bool m_fill;
    double m_rate;
    bool m_enableHWA;
    int m_posNotifyInterval;
    bool m_posNotifyWithSec;
    bool m_showFirstFrame;
    QSize m_renderSize;
    QString m_vsinkName;
    QString m_vcodecName;
    QString m_acodecName;
    int m_timeout;

    qint64 m_lastNotifyPos;
    GstState m_targetState;
    bool m_isDestoryed;
    bool m_isReqPlay;
    bool m_isReqSeek;
    bool m_isForceEnd;
    bool m_isEos;

    int m_probeCheckInterval;
    int m_probeCheckTimeout;
    int m_probeCheckMaxErrTimes;
    int m_probeCheckCurErrTimes;
    QDateTime m_probeLastDateTime;

    friend gboolean gstBusMessageCallback(GstBus *bus, GstMessage *msg, gpointer data);
    friend void gstPadAddedCallback(GstElement *src, GstPad *new_pad, gpointer data);
    friend int gstDecoderSelectCallback(GstElement *bin, GstPad *pad, GstCaps* caps, GstElementFactory* factory, gpointer data);
};

#endif

